#include "StdAfx.h"


#ifndef EXCLUDE_SCALEFORM_SDK


#include "GMemorySTLAlloc.h"
#include "GTextureXRender.h"
#include "GRendererXRender.h"
#include "FlashPlayerInstance.h"
#include "SharedStates.h"
#include <StringUtils.h>
#include <GImage.h>

#include <IRenderer.h>
#include <ISystem.h>
#include <ILog.h>
#include <vector>

#ifndef RELEASE
#include <IFlashUI.h>
#endif






static inline ETEX_Format MapImageType(const GImageBase::ImageFormat& fmt)
{
	switch (fmt)
	{
	case GImageBase::Image_ARGB_8888:
		return eTF_A8R8G8B8;
	case GImageBase::Image_RGB_888:
		return eTF_R8G8B8;
	case GImageBase::Image_L_8:
		return eTF_L8;
	case GImageBase::Image_A_8:
		return eTF_A8;
	case GImageBase::Image_DXT1:
		return eTF_DXT1;
	case GImageBase::Image_DXT3:
		return eTF_DXT3;
	case GImageBase::Image_DXT5:
		return eTF_DXT5;
	default:
		return eTF_Unknown;
	}
}


//////////////////////////////////////////////////////////////////////////
// GTextureXRenderBase

#if defined(ENABLE_FLASH_INFO)
volatile int32 GTextureXRenderBase::ms_textureMemoryUsed(0);
volatile int32 GTextureXRenderBase::ms_numTexturesCreated(0);
volatile int32 GTextureXRenderBase::ms_numTexturesCreatedTotal(0);
volatile int32 GTextureXRenderBase::ms_numTexturesCreatedTemp(0);
volatile int32 GTextureXRenderBase::ms_fontCacheTextureID(0);
#endif


GTextureXRenderBase::GTextureXRenderBase(GRendererXRender* pRendererXRender, int texID, ETEX_Format fmt, bool isTemp)
: m_texID(texID)
, m_fmt(fmt)
, m_userData(0)
, m_pRendererXRender(pRendererXRender)
#if defined(ENABLE_FLASH_INFO)
, m_isTemp(isTemp)
#endif
{
	assert(m_pRendererXRender);
#if defined(ENABLE_FLASH_INFO)
	CryInterlockedIncrement(&ms_numTexturesCreated);
	if (isTemp)
		CryInterlockedIncrement(&ms_numTexturesCreatedTemp);
	else
		CryInterlockedIncrement(&ms_numTexturesCreatedTotal);
#endif
	m_pRendererXRender->AddRef();
}


GTextureXRenderBase::~GTextureXRenderBase()
{
#if defined(ENABLE_FLASH_INFO)
	CryInterlockedDecrement(&ms_numTexturesCreated);
	if (m_isTemp)
		CryInterlockedDecrement(&ms_numTexturesCreatedTemp);
#endif
	m_pRendererXRender->Release();
}


GRenderer* GTextureXRenderBase::GetRenderer() const
{
	return m_pRendererXRender;
}


//////////////////////////////////////////////////////////////////////////
// GTextureXRender

GTextureXRender::GTextureXRender(GRendererXRender* pRendererXRender, int texID, ETEX_Format fmt)
: GTextureXRenderBase(pRendererXRender, texID, fmt)
{
}


GTextureXRender::~GTextureXRender()
{
	if (m_texID > 0)
	{
		IRenderer* pRenderer(m_pRendererXRender->GetXRender());
		ITexture* pTexture(pRenderer->EF_GetTextureByID(m_texID));
		assert(pTexture);
#if defined(ENABLE_FLASH_INFO)
		if (!pTexture->IsShared())
		{
			int size = pTexture->GetDeviceDataSize();
			CryInterlockedAdd(&ms_textureMemoryUsed, -size);
		}
		if (ms_fontCacheTextureID == m_texID)
			ms_fontCacheTextureID = 0;
#endif
		pRenderer->RT_FlashRemoveTexture(pTexture);
	}
}


bool GTextureXRender::InitTexture(GImageBase* pIm, UInt /*usage*/)
{
	assert(m_texID == -1);
	if (InitTextureInternal(pIm))
	{
		IRenderer* pRenderer(m_pRendererXRender->GetXRender());
		ITexture* pTexture(pRenderer->EF_GetTextureByID(m_texID));
		assert(pTexture && !pTexture->IsShared());
#if defined(ENABLE_FLASH_INFO)
		int size = pTexture->GetDeviceDataSize();
		CryInterlockedAdd(&ms_textureMemoryUsed, size);
#endif
		m_fmt = pTexture->GetTextureSrcFormat();
	}
	return m_texID > 0;
}


bool GTextureXRender::InitTextureFromFile(const char* pFilename)
{
	assert(m_texID == -1);

	string sFile = pFilename;
	PathUtil::ToUnixPath(sFile);
	while (sFile.find("//") != stack_string::npos)
		sFile.replace("//","/");
	pFilename = sFile.c_str();

	IRenderer* pRenderer(m_pRendererXRender->GetXRender());
	ITexture* pTexture(pRenderer->EF_LoadTexture(pFilename, FT_DONT_STREAM | FT_DONT_RESIZE | FT_NOMIPS));
	if (pTexture)
	{
#ifndef RELEASE
		if (gEnv->pFlashUI)
		{
			gEnv->pFlashUI->CheckPreloadedTexture(pTexture);
		}
#endif
		m_texID = pTexture->GetTextureID();
#if defined(ENABLE_FLASH_INFO)
		if (!pTexture->IsShared())
		{
			int size = pTexture->GetDeviceDataSize();
			CryInterlockedAdd(&ms_textureMemoryUsed, size);
		}
#endif
		m_fmt = pTexture->GetTextureSrcFormat();
	}
	return m_texID > 0;
}


bool GTextureXRender::InitTextureFromTexId(int texid)
{
	assert(m_texID == -1);
	IRenderer* pRenderer(m_pRendererXRender->GetXRender());
	ITexture* pTexture(pRenderer->EF_GetTextureByID( texid ));
	if (pTexture)
	{
		pTexture->AddRef();
		m_texID = pTexture->GetTextureID();
#if defined(ENABLE_FLASH_INFO)
		if (!pTexture->IsShared())
		{
			int size = pTexture->GetDeviceDataSize();
			CryInterlockedAdd(&ms_textureMemoryUsed, size);
		}
#endif
		m_fmt = pTexture->GetTextureSrcFormat();
	}
	return m_texID > 0;
}


bool GTextureXRender::InitTextureInternal(const GImageBase* pIm)
{
	ETEX_Format texFmt(MapImageType(pIm->Format));
	if (texFmt != eTF_Unknown)
	{
		return InitTextureInternal(texFmt, pIm->Width, pIm->Height, pIm->Pitch, pIm->pData);
	}
	else
	{
		gEnv->pLog->LogWarning("<Flash> GTextureXRender::InitTextureInternal( ... ) -- Attempted to load texture with unsupported texture format!\n");
		return false;
	}
}


bool GTextureXRender::InitTextureInternal(ETEX_Format texFmt, int32 width, int32 height, int32 pitch, uint8* pData)
{
	IRenderer* pRenderer(m_pRendererXRender->GetXRender());

	bool rgba((pRenderer->GetFeatures() & RFT_RGBA) != 0 || pRenderer->GetRenderType() == eRT_DX11);
	bool swapRB(!rgba && texFmt == eTF_A8R8G8B8);
	ETEX_Format texFmtOrig(texFmt);

	// expand RGB to RGBA if necessary
	std::vector<uint8, GMemorySTLAlloc<uint8> > expandRGB8;
	if (texFmt == eTF_R8G8B8)
	{
		expandRGB8.reserve(width * height * 4);
		for (int32 y(0); y<height; ++y)
		{
			uint8* pCol(&pData[pitch * y]);
			if (rgba)
			{
				for (int32 x(0); x<width; ++x, pCol += 3)
				{
					expandRGB8.push_back(pCol[0]);
					expandRGB8.push_back(pCol[1]);
					expandRGB8.push_back(pCol[2]);
					expandRGB8.push_back(255);
				}
			}
			else
			{
				for (int32 x(0); x<width; ++x, pCol += 3)
				{
					expandRGB8.push_back(pCol[2]);
					expandRGB8.push_back(pCol[1]);
					expandRGB8.push_back(pCol[0]);
					expandRGB8.push_back(255);
				}
			}
		}
		
		pData = &expandRGB8[0];
		pitch = width * 4;
		texFmt = eTF_X8R8G8B8;
		swapRB = false;
	}

	if (swapRB)
		SwapRB(pData, width, height, pitch);






	// Create texture...
	// Note that mip-maps should be generated for font textures (A8/L8) as 
	// Scaleform generates font textures only once and relies on mip-mapping
	// to implement various font sizes.
	m_texID = pRenderer->SF_CreateTexture(width, height, (texFmt == eTF_A8 || texFmt == eTF_L8) ? 0 : 1, pData, texFmt, FT_DONT_STREAM | FT_DONT_RESIZE);

	if (m_texID <= 0)
	{
		static const char* s_fomats[] =
		{
			"Unknown",
			"A8R8G8B8",
			"R8G8B8->X8R8G8B8",
			"L8",
			"A8",
			"DXT1",
			"DXT3",
			"DXT5",
			0
		};

		int fmtIdx(0);
		switch (texFmtOrig)
		{
		case eTF_A8R8G8B8:
				fmtIdx = 1;
				break;
		case eTF_X8R8G8B8:
				fmtIdx = 2;
				break;
		case eTF_L8:
				fmtIdx = 3;
				break;
		case eTF_A8:
				fmtIdx = 4;
				break;
		case eTF_DXT1:
				fmtIdx = 5;
				break;
		case eTF_DXT3:
				fmtIdx = 6;
				break;
		case eTF_DXT5:
				fmtIdx = 7;
				break;
		}

		gEnv->pLog->LogWarning("<Flash> GTextureXRender::InitTextureInternal( ... ) "
			"-- Texture creation failed (%dx%d, %s)\n", width, height, s_fomats[fmtIdx]);
	}






	if (swapRB)
		SwapRB(pData, width, height, pitch);

	return m_texID > 0;
}


bool GTextureXRender::InitDynamicTexture(int width, int height, GImage::ImageFormat format, int mipmaps, UInt usage)
{
	// Currently only used for font cache texture. Enforce global heap usage. In the game that
	// is given automatically since the menu first creates this. However, chainloading doesn't
	// and the texture ends up in the level heap resulting in "false positives" being reported.
	ScopedSwitchToGlobalHeap useGlobalHeap;

	if (Usage_Update == usage)
	{
		IRenderer* pRenderer(m_pRendererXRender->GetXRender());
		assert(m_texID == -1);
		m_texID = pRenderer->SF_CreateTexture(width, height, mipmaps + 1, 0, MapImageType(format), FT_DONT_STREAM | FT_DONT_RESIZE);
		if (m_texID > 0)
		{
			ITexture* pTexture(pRenderer->EF_GetTextureByID(m_texID));
			assert(pTexture && !pTexture->IsShared());
#if defined(ENABLE_FLASH_INFO)
			int size = pTexture->GetDeviceDataSize();
			CryInterlockedAdd(&ms_textureMemoryUsed, size);
			assert(!ms_fontCacheTextureID);
			ms_fontCacheTextureID = m_texID;
#endif
			m_fmt = pTexture->GetTextureSrcFormat();
		}
	}
	else
	{
		assert(0 && "GTextureXRender::InitDynamicTexture() -- Unsupported usage flag!");
	}
	return m_texID > 0;
}


void GTextureXRender::Update(int level, int n, const UpdateRect* pRects, const GImageBase* pIm)
{
	FUNCTION_PROFILER_FAST(GetISystem(),PROFILE_SCALEFORM, g_bProfilerEnabled);

	assert(m_texID > 0);
	if (!pRects || !n || !pIm || m_texID <= 0)
		return;

	PREFAST_SUPPRESS_WARNING(6255)
	IRenderer::SUpdateRect* pSrcRects((IRenderer::SUpdateRect*) alloca(n * sizeof(IRenderer::SUpdateRect)));
	if (pSrcRects)
	{
		for (int i(0); i<n; ++i)
			pSrcRects[i].Set(pRects[i].dest.x, pRects[i].dest.y, pRects[i].src.Left, pRects[i].src.Top, pRects[i].src.Width(), pRects[i].src.Height());

		IRenderer* pRenderer(m_pRendererXRender->GetXRender());
		pRenderer->SF_UpdateTexture(m_texID, level, n, pSrcRects, pIm->pData, pIm->Pitch, MapImageType(pIm->Format));
	}
}


int GTextureXRender::Map(int /*level*/, int /*n*/, MapRect* /*maps*/, int /*flags*/)
{
	assert(0 && "GTextureXRender::Map() -- Not implemented!");
	return 0;
}


bool GTextureXRender::Unmap(int /*level*/, int /*n*/, MapRect* /*maps*/, int /*flags*/)
{
	assert(0 && "GTextureXRender::Unmap() -- Not implemented!");
	return false;
}


void GTextureXRender::SwapRB(uint8* pImageData, uint32 width, uint32 height, uint32 pitch) const
{
	for (uint32 y(0); y<height; ++y)
	{
		uint8* pCol(&pImageData[pitch * y]);
		for (uint32 x(0); x<width; ++x, pCol += 4)
		{
			uint8 s(pCol[0]);
			pCol[0] = pCol[2];
			pCol[2] = s;
		}
	}
}













//////////////////////////////////////////////////////////////////////////
// GTextureXRenderTempRT
GTextureXRenderTempRT::GTextureXRenderTempRT(GRendererXRender* pRendererXRender, int texID, ETEX_Format fmt)
	: GTextureXRender(pRendererXRender, texID, fmt)
{
}

GTextureXRenderTempRT::~GTextureXRenderTempRT()
{
}


//////////////////////////////////////////////////////////////////////////
// GTextureXRenderTempRTLockless
GTextureXRenderTempRTLockless::GTextureXRenderTempRTLockless(GRendererXRender* pRendererXRender)
	: GTextureXRenderBase(pRendererXRender, -1, eTF_Unknown, true)
	, m_pRT(0)
{
}

GTextureXRenderTempRTLockless::~GTextureXRenderTempRTLockless()
{
	m_pRT = 0;
}

void GTextureXRenderTempRTLockless::SetRT(GTexture* pRT)
{
	m_pRT = pRT;
}


//////////////////////////////////////////////////////////////////////////
// GTextureXRenderYUV

GTextureXRenderYUV::GTextureXRenderYUV(GRendererXRender* pRendererXRender)
: GTextureXRenderBase(pRendererXRender)
//, m_texIDs()
, m_numIDs(0)
, m_width(0)
, m_height(0)
, m_isStereoContent(false)
{
	m_texID = 0;
	m_texIDs[0] = m_texIDs[1] = m_texIDs[2] = m_texIDs[3] = -1;
}


GTextureXRenderYUV::~GTextureXRenderYUV()
{
	IRenderer* pRenderer = m_pRendererXRender->GetXRender();
	for (int32 i=0; i<m_numIDs; ++i)
	{
		int32 texId = m_texIDs[i];
		if (texId > 0)
		{
			ITexture* pTexture = pRenderer->EF_GetTextureByID(texId);
			assert(pTexture);
#if defined(ENABLE_FLASH_INFO)
			if (!pTexture->IsShared())
			{
				int size = pTexture->GetDeviceDataSize();
				CryInterlockedAdd(&ms_textureMemoryUsed, -size);
			}
#endif
			pRenderer->RT_FlashRemoveTexture(pTexture);
		}
	}
}


bool GTextureXRenderYUV::InitTexture(GImageBase* /*pIm*/, UInt /*usage*/)
{
	assert(0 && "GTextureXRenderYUV::InitTexture() -- Not implemented!");
	return false;
}


bool GTextureXRenderYUV::InitTextureFromFile(const char* /*pFilename*/)
{
	assert(0 && "GTextureXRenderYUV::InitTextureFromFile() -- Not implemented!");
	return false;
}


bool GTextureXRenderYUV::InitTextureFromTexId(int /*texid*/)
{
	assert(0 && "GTextureXRenderYUV::InitTextureFromTexId() -- Not implemented!");
	return false;
}


void GTextureXRenderYUV::Clear()
{
	static const unsigned char clearVal[] = {0, 128, 128, 255};

	const int level = 0; // currently don't have mips so only clear level 0

	IRenderer* pRenderer = m_pRendererXRender->GetXRender();

	for (int32 i=0; i<m_numIDs; ++i)
	{
		const uint32 width = Res(i, m_width);
		const uint32 height = Res(i, m_height);

		void* pBits = 0; uint32 pitch = 0;
		if (pRenderer->SF_MapTexture(m_texIDs[i], level, pBits, pitch))
		{
			assert(pBits);
			char* pDst = (char*) pBits;
			for (uint32 y=0; y<height; ++y, pDst+=pitch)
				memset(pDst, clearVal[i], width);
			pRenderer->SF_UnmapTexture(m_texIDs[i], level);
		}
	}
}


bool GTextureXRenderYUV::InitDynamicTexture(int width, int height, GImage::ImageFormat format, int mipmaps, UInt usage)
{
	assert(format == GImageBase::Image_RGB_888 || format == GImageBase::Image_ARGB_8888);
	if (Usage_Map == usage && (format == GImageBase::Image_RGB_888 || format == GImageBase::Image_ARGB_8888))
	{
		IRenderer* pRenderer = m_pRendererXRender->GetXRender();
		bool ok = true;
#if !defined(XENON)
		const ETEX_Format fmt = eTF_A8;


#endif
		int32 textureMemoryUsed = 0;
		const int32 numTex = format == GImage::Image_ARGB_8888 ? 4 : 3;
		for (int32 i=0; i<numTex; ++i)
		{
			assert(m_texIDs[i] == -1);
			int w = Res(i, (uint32) width);
			int h = Res(i, (uint32) height);
			int32 texId = pRenderer->SF_CreateTexture(w, h, mipmaps + 1, 0, fmt, FT_DONT_STREAM | FT_DONT_RESIZE | FT_USAGE_DYNAMIC);
			ok = texId > 0;
			if (ok)
			{
				ITexture* pTexture(pRenderer->EF_GetTextureByID(texId));
				assert(pTexture && !pTexture->IsShared());
				textureMemoryUsed += pTexture->GetDeviceDataSize();
				m_texIDs[i] = texId;
			}
			else
				break;
		}

		if (ok)
		{
			m_width = (uint32) width;
			m_height = (uint32) height;
			m_numIDs = numTex;
			m_fmt = GImage::Image_ARGB_8888 ? eTF_YUVA : eTF_YUV;
#if defined(ENABLE_FLASH_INFO)
			CryInterlockedAdd(&ms_textureMemoryUsed, textureMemoryUsed);
#endif
			Clear();
		}
		else
		{
			for (int32 i=0; i<numTex; ++i)
			{
				int32 texId = m_texIDs[i];
				if (texId > 0)
				{
					ITexture* pTexture = pRenderer->EF_GetTextureByID(texId);
					assert(pTexture);
					pRenderer->RT_FlashRemoveTexture(pTexture);
				}
				else
					break;
			}
		}
	}
	else
	{
		assert(0 && "GTextureXRenderYUV::InitDynamicTexture() -- Unsupported usage flag or format!");
	}

	return m_numIDs > 0;
}


void GTextureXRenderYUV::Update(int /*level*/, int /*n*/, const UpdateRect* /*pRects*/, const GImageBase* /*pIm*/)
{
	assert(0 && "GTextureXRenderYUV::Update() -- Not implemented!");
}


int GTextureXRenderYUV::Map(int level, int n, MapRect* maps, int /*flags*/)
{
	assert(m_numIDs > 0 && maps && !level && n >= m_numIDs);
	if (m_numIDs <= 0 || !maps || level > 0 || n < m_numIDs)
		return 0;

	IRenderer* pRenderer = m_pRendererXRender->GetXRender();
	bool ok = true;
	for (int32 i=0; i<m_numIDs; ++i)
	{
		void* pBits = 0; uint32 pitch = 0;
		ok = pRenderer->SF_MapTexture(m_texIDs[i], level, pBits, pitch);
		if (ok)
		{
			maps[i].width = Res(i, m_width);
			maps[i].height = Res(i, m_height);
			maps[i].pData = (UByte*) pBits;
			maps[i].pitch = pitch;
		}
		else
			break;
	}
	return ok ? m_numIDs : 0;
}


bool GTextureXRenderYUV::Unmap(int level, int n, MapRect* maps, int /*flags*/)
{
	assert(m_numIDs > 0);
	if (m_numIDs <= 0)
		return false;





























	GUNUSED2(n, maps);


	IRenderer* pRenderer = m_pRendererXRender->GetXRender();
	for (int32 i=0; i < m_numIDs; ++i)
	{
		pRenderer->SF_UnmapTexture(m_texIDs[i], level);
	}
	return true;
}


#endif // #ifndef EXCLUDE_SCALEFORM_SDK